<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
            "http://www.w3.org/TR/html4/strict.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>ScpTreeStore</title>
<style type="text/css">
	.tab { padding-left:2em; }
	table { border-collapse:collapse; }
	th, td { border: 1px solid black; }
	.empty { border: 0px; }
	th, td { padding: 0em 0.5em; }
</style>
</head>

<body>

<div><a href="#description">Description</a> <b>--</b> <a href="#synopsis">Synopsis</a>
<b>--</b> <a href="#properties">Properties</a> <b>--</b> <a href="#notes">Notes</a> <b>--</b>
<a href="#speed">Speed</a> <b>--</b> <a href="#bugs">Bugs</a> <b>--</b> <a href="#copyright">
Copyright</a></div>

<h2>ScpTreeStore</h2>

<p>ScpTreeStore - an array-based GtkTreeStore/GtkListStore like object.</p>

<hr>

<h3><a name="description">Description</a></h3>

<p>ScpTreeStore is a GtkTreeStore/GtkListStore object, based entitely on arrays. In
particular:</p>

<ul>
<li>The column values for a row are stored in a C array</li>
<li>The column sort headers are stored in a C array</li>
<li>The rows are stored in GPtrArray-s.</li>
</ul>

<p>Using arrays for the column headers and row data would probably be beneficial for
GtkTreeStore and GtkListStore too, leading to faster and smaller code.</p>

<p>ScpTreeStore implements GtkTreeModel, GtkTreeDragSource, GtkTreeDragDest, GtkTreeSortable
and GtkBuildable. The functions that implement these interfaces are public, so you can invoke
them directly, without type-casting the store to an interface (except for the GtkBuildable
functions, which are never invoked directly).</p>

<hr>

<h3><a name="synopsis">Synopsis</a></h3>

<p>The functions identical to their GtkTreeStore or interface counterparts, as well as the
obvious funtions and macros, are listed without descriptions.</p>

<p>The GtkTreeStore behviour described below applies to GtkListStore too (where applicable).
</p>

<p>#include &lt;gtk/gtk.h&gt;<br>
#include &quot;scptreestore.h&quot;</p>

<p>/* Store */<br>
ScpTreeStore *<a href="#scp_tree_store_new">scp_tree_store_new</a>(gboolean sublevels, gint
n_columns, ...);<br>
ScpTreeStore *<a href="#scp_tree_store_newv">scp_tree_store_newv</a>(gboolean sublevels, gint
n_columns, GType *types);<br>
gboolean scp_tree_store_set_column_types(ScpTreeStore *store, gint n_columns, GType *types);<br>
void <a href="#scp_tree_store_set_utf8_collate">scp_tree_store_set_utf8_collate</a>(ScpTreeStore
*store, gint column, gboolean collate);<br>
void scp_tree_store_set_valuesv(ScpTreeStore *store, GtkTreeIter *iter, gint *columns, GValue
*values, gint n_values);<br>
void scp_tree_store_set_value(ScpTreeStore *store, GtkTreeIter *iter, gint column, GValue
*value);<br>
void scp_tree_store_set_valist(ScpTreeStore *store, GtkTreeIter *iter, va_list ap);<br>
void scp_tree_store_set(ScpTreeStore *store, GtkTreeIter *iter, ...);<br>
gboolean <a href="#scp_tree_store_remove">scp_tree_store_remove</a>(ScpTreeStore *store,
GtkTreeIter *iter);<br>
void <a href="#scp_tree_store_insert">scp_tree_store_insert</a>(ScpTreeStore *store, GtkTreeIter
*iter, GtkTreeIter *parent, gint position);<br>
#define scp_tree_store_prepend(store, iter, parent)<br>
#define scp_tree_store_append(store, iter, parent)<br>
void scp_tree_store_insert_with_valuesv(ScpTreeStore *store, GtkTreeIter *iter, GtkTreeIter
*parent, gint position, gint *columns, GValue *values, gint n_values);<br>
#define scp_tree_store_prepend_with_valuesv(store, iter, parent, columns, values, n_values)<br>
#define scp_tree_store_append_with_valuesv(store, iter, parent, columns, values, n_values)<br>
void scp_tree_store_insert_with_valist(ScpTreeStore *store, GtkTreeIter *iter, GtkTreeIter
*parent, gint position, va_list ap);<br>
#define scp_tree_store_prepend_with_valist(store, iter, parent, ap)<br>
#define scp_tree_store_append_with_valist(store, iter, parent, ap)<br>
void scp_tree_store_insert_with_values(ScpTreeStore *store, GtkTreeIter *iter, GtkTreeIter
*parent, gint position, ...);<br>
#define scp_tree_store_prepend_with_values(store, iter, parent, ...)<br>
#define scp_tree_store_append_with_values(store, iter, parent, ...)<br>
void <a href="#scp_tree_store_get_valist">scp_tree_store_get_valist</a>(ScpTreeStore *store,
GtkTreeIter *iter, va_list var_args);<br>
void <a href="#scp_tree_store_get">scp_tree_store_get</a>(ScpTreeStore *store, GtkTreeIter
*iter, ...);<br>
gboolean scp_tree_store_is_ancestor(ScpTreeStore *store, GtkTreeIter *iter, GtkTreeIter
*descendant);<br>
gint scp_tree_store_iter_depth(ScpTreeStore *store, GtkTreeIter *iter);<br>
void <a href="#scp_tree_store_clear_children">scp_tree_store_clear_children</a>(ScpTreeStore
*store, GtkTreeIter *parent, gboolean emit_subsignals);<br>
#define <a href="#scp_tree_store_clear">scp_tree_store_clear(store)</a><br>
gboolean scp_tree_store_iter_is_valid(ScpTreeStore *store, GtkTreeIter *iter);<br>
void scp_tree_store_reorder(ScpTreeStore *store, GtkTreeIter *parent, gint *new_order);<br>
void scp_tree_store_swap(ScpTreeStore *store, GtkTreeIter *a, GtkTreeIter *b);<br>
void <a href="#scp_tree_store_move">scp_tree_store_move</a>(ScpTreeStore *store, GtkTreeIter
*iter, gint position);<br>
</p>

<p>/* Model */<br>
GtkTreeModelFlags scp_tree_store_get_flags(ScpTreeStore *store);<br>
gint scp_tree_store_get_n_columns(ScpTreeStore *store);<br>
GType scp_tree_store_get_column_type(ScpTreeStore *store, gint index);<br>
gboolean scp_tree_store_get_iter(ScpTreeStore *store, GtkTreeIter *iter, GtkTreePath *path);<br>
GtkTreePath *scp_tree_store_get_path(ScpTreeStore *store, GtkTreeIter *iter);<br>
void scp_tree_store_get_value(ScpTreeStore *model, GtkTreeIter *iter, gint column, GValue
*value);<br>
gboolean scp_tree_store_iter_next(ScpTreeStore *store, GtkTreeIter *iter);<br>
gboolean <a href="#scp_tree_store_iter_previous">scp_tree_store_iter_previous</a>(ScpTreeStore
*store, GtkTreeIter *iter);<br>
gboolean scp_tree_store_iter_children(ScpTreeStore *store, GtkTreeIter *iter, GtkTreeIter
*parent);<br>
gboolean scp_tree_store_iter_has_child(ScpTreeStore *store, GtkTreeIter *iter);<br>
gint scp_tree_store_iter_n_children(ScpTreeStore *store, GtkTreeIter *iter);<br>
gboolean scp_tree_store_iter_nth_child(ScpTreeStore *store, GtkTreeIter *iter, GtkTreeIter
*parent, gint n);<br>
#define scp_tree_store_get_iter_first(store, iter)<br>
gboolean scp_tree_store_iter_parent(ScpTreeStore *store, GtkTreeIter *iter, GtkTreeIter
*child);<br>
void scp_tree_store_foreach(ScpTreeStore *store, GtkTreeModelForeachFunc func, gpointer
gdata);<br>
</p>

<p>/* DND */<br>
gboolean scp_tree_store_row_draggable(ScpTreeStore *store, GtkTreePath *path);<br>
gboolean scp_tree_store_drag_data_delete(ScpTreeStore *store, GtkTreePath *path);<br>
gboolean scp_tree_store_drag_data_get(ScpTreeStore *store, GtkTreePath *path, GtkSelectionData
*selection_data);<br>
gboolean scp_tree_store_drag_data_received(ScpTreeStore *store, GtkTreePath *dest,
GtkSelectionData *selection_data);<br>
gboolean scp_tree_store_row_drop_possible(ScpTreeStore *store, GtkTreePath *dest_path,
GtkSelectionData *selection_data);<br>
</p>

<p>/* Sortable */<br>
gboolean scp_tree_store_get_sort_column_id(ScpTreeStore *store, gint *sort_column_id,
GtkSortType *order);<br>
void scp_tree_store_set_sort_column_id(ScpTreeStore *store, gint sort_column_id, GtkSortType
order);<br>
void scp_tree_store_set_sort_func(ScpTreeStore *store, gint sort_column_id,
GtkTreeIterCompareFunc func, gpointer data, GDestroyNotify destroy);<br>
void scp_tree_store_set_default_sort_func(ScpTreeStore *store, GtkTreeIterCompareFunc func,
gpointer data, GDestroyNotify destroy);<br>
gboolean scp_tree_store_has_default_sort_func(ScpTreeStore *store);<br>
</p>

<p>/* Extra */<br>
void <a href="#scp_tree_store_set_allocation">scp_tree_store_set_allocation</a>(ScpTreeStore
*store, guint toplevel_reserved, guint sublevel_reserved, gboolean sublevel_discard);<br>
gint <a href="#scp_tree_store_compare_func">scp_tree_store_compare_func</a>(ScpTreeStore *store,
GtkTreeIter *a, GtkTreeIter *b, gpointer data);<br>
gboolean <a href="#scp_tree_store_iter_seek">scp_tree_store_iter_seek</a>(ScpTreeStore *store,
GtkTreeIter *iter, gint position);<br>
gint scp_tree_store_iter_tell(ScpTreeStore *store, GtkTreeIter *iter);<br>
gboolean <a href="#scp_tree_store_search">scp_tree_store_search</a>(ScpTreeStore *store,
gboolean sublevels, gboolean linear_order, GtkTreeIter *iter, GtkTreeIter *parent, gint column,
...);<br>
typedef gint (*<a href="#ScpTreeStoreTraverseFunc">ScpTreeStoreTraverseFunc</a>)(ScpTreeStore
*store, GtkTreeIter *iter, gpointer gdata);<br>
gboolean <a href="#scp_tree_store_traverse">scp_tree_store_traverse</a>(ScpTreeStore *store,
gboolean sublevels, GtkTreeIter *iter, GtkTreeIter *parent, ScpTreeStoreTraverseFunc func,
gpointer gdata);
</p>

<hr>

<h3><a name="scp_tree_store_new">scp_tree_store_new()</a><br>
<a name="scp_tree_store_newv">scp_tree_store_newv()</a></h3>

<p><b>ScpTreeStore *scp_tree_store_new(gboolean sublevels, gint n_columns, ...);<br>
ScpTreeStore *scp_tree_store_newv(gboolean sublevels, gint n_columns, GType *types);</b></p>

<div>The sublevels parameter determines whether ScpTreeStore behaves as GtkTreeStore or
GtkListStore:</div>
<div class="tab">sublevels = <tt>TRUE</tt>: tree-like structure<br>
sublevels = <tt>FALSE</tt>: list-like structure.</div>
<p></p>

<hr>

<h3><a name="scp_tree_store_insert">scp_tree_store_insert()</a></h3>

<p><b>void scp_tree_store_insert(ScpTreeStore *store, GtkTreeIter *iter, GtkTreeIter *parent,
gint position);</b></p>

<p>position must be between -1 and N, where N is the number of rows at that level.
GtkTreeStore allows position &gt; N and treats it as N (i.e. appends). To be precise, it
simply walks it's row-list either until position is reached or there are no more rows.</p>

<p>If you insert a row in a sorted GtkTreeStore, and do not set a value for the sort column
(or for any column, if a non-standard sort function is being used), the row will remain where
you inserted it, possibly breaking the sort order. ScpTreeStore will sort such rows if needed.
</p>

<p>When a row is being sorted as a result of an insert_with or set operation, GtkTreeStore
uses linear search, so this row always end up before the first row equal to it, if any.
ScpTreeStore uses binary search, and "If two iters compare as equal, their order in the sorted
model is undefined", as stated in the GtkTreeSortable documentation.</p>

<hr>

<h3><a name="scp_tree_store_remove">scp_tree_store_remove()</a></h3>

<p><b>void scp_tree_store_remove(ScpTreeStore *store, GtkTreeIter *iter);</b></p>

<p>GtkTreeStore removes the children of iter (if any) from first to last, while ScpTreeStore
removes them from last to first. The GTK+ documentation does not specify any particular order.
</p>

<hr>

<h3><a name="scp_tree_store_get_valist">scp_tree_store_get_valist()</a><br>
<a name="scp_tree_store_get">scp_tree_store_get()</a></h3>

<p><b>void scp_tree_store_get_valist(ScpTreeStore *store, GtkTreeIter *iter, va_list
var_args);<br>
void scp_tree_store_get(ScpTreeStore *store, GtkTreeIter *iter, ...);</b></p>

<p>Unlike the similar GtkTreeModel functions, these do not copy the strings or reference the
objects/boxeds, so the return values for these types (a) need not be freed/unreferenced, and
(b) may become invalid if the row is removed.</p>

<hr>

<h3><a name="scp_tree_store_clear_children">scp_tree_store_clear_children()</a></h3>

<p><b>void scp_tree_store_clear_children(ScpTreeStore *store, GtkTreeIter *parent, gboolean
emit_subsignals);</b></p>

<div>Removes all rows under parent, from last to first.</div>
<div class="tab">emit_subsignals = <tt>FALSE</tt>: emit &quot;row-removed&quot; signals only
for the rows directly under parent<br>
emit_subsignals = <tt>TRUE</tt>: emit &quot;row-removed&quot; signals for all removed rows<br>
parent = <tt>NULL</tt>: remove the top-level rows.</div>
<p></p>

<hr>

<h3><a name="scp_tree_store_clear">scp_tree_store_clear()</a></h3>

<p><b>#define scp_tree_store_clear(store)</b></p>

<p>gtk_tree_store_clear() emits &quot;row-removed&quot; signals not only for the top-level
rows, but for their all their children as well. ScpTreeStore follows that behaviour. Clearing
the store with scp_tree_store_clear_children(store, NULL, FALSE) is faster, though not
significantly.</p>

<p>GtkTreeStore removes the rows from first to last, while ScpTreeStore removes them from last
to first. The GTK+ documentation does not specify any particular order.</p>

<hr>

<h3><a name="void scp_tree_store_move">void scp_tree_store_move()</a></h3>

<p><b>void scp_tree_store_move(ScpTreeStore *store, GtkTreeIter *iter, gint position);</b></p>

<div>Moves the row pointed by iter to a new position in the same level.</div>
<div class="tab">position = <tt>-1</tt>: move row to the last position.</div>
<p></p>

<hr>

<h3><a name="scp_tree_store_iter_previous">scp_tree_store_iter_previous()</a></h3>

<p><b>gboolean scp_tree_store_iter_previous(ScpTreeStore *store, GtkTreeIter *iter);</b></p>

<p>gtk_tree_model_iter_previous() requires gtk+ 3.0.0 or later; scp_tree_model_iter_previous()
does not.</p>

<hr>

<h3><a name="scp_tree_store_set_allocation">scp_tree_store_set_allocation()</a></h3>

<p><b>void scp_tree_store_set_reserved(ScpTreeStore *store, guint toplevel_reserved, guint
sublevel_reserved, gboolean sublevel_discard);</b></p>

<div>Sets the &quot;toplevel-reserved&quot;, &quot;sublevel-reserved&quot; and
&quot;sublevel_discard&quot; <a href="#properties">properties</a>.</div>
<div class="tab"> toplevel_reserved = <tt>0</tt>: &quot;toplevel-reserved&quot; will not be
set (the default is 0 anyway).</div>
<p></p>

<hr>

<h3><a name="scp_tree_store_set_utf8_collate">scp_tree_store_set_utf8_collate()</a></h3>

<p><b>void scp_tree_store_set_utf8_collate(ScpTreeStore *store, gint column, gboolean
collate);</b></p>

<div>Normally, all stores compare strings with g_utf8_collate(), which is linguistically
correct, but very slow. You can create a separate column with the string keys, of course, and
write a small compare function that does g_strcmp0(), but the side effects are that in a store
is sorted by a non-standard function, setting the value of any column causes a check if the
row is properly sorted, and ScpTreeStore's binary search can not be used.</div>
<div class="tab">collate = <tt>TRUE</tt>: use g_utf8_collate() to compare strings, only for
string columns<br>
collate = <tt>FALSE</tt>: use g_strcmp0() to compare strings, the above side effects mentioned
above.
</div>

<hr>

<h3><a name="scp_tree_store_compare_func">scp_tree_store_compare_func()</a></h3>

<p><b>gint scp_tree_store_compare_func(ScpTreeStore *store, GtkTreeIter *a, GtkTreeIter *b,
gpointer data);</b></p>

<div>The default ScpTreeStore data comparision function for columns &gt;= 0.</div>
<div class="tab">data = <tt>GINT_TO_POINTER(column_to_compare)</tt></div>
<p>May NOT be used with GtkTreeStore and GtkListStore.<br>
Compatible with and type-castable to GtkTreeIterCompareFunc.</p>

<hr>

<h3><a name="scp_tree_store_iter_seek">scp_tree_store_iter_seek()</a></h3>

<p><b>gboolean scp_tree_store_iter_seek(ScpTreeStore *store, GtkTreeIter *iter, gint
position);</b></p>

<div>Seek to row at position in the same level.</div>
<div class="tab">position = -1: seek to the last row.</div>
<p></p>

<hr>

<h3><a name="scp_tree_store_search">scp_tree_store_search()</a></h3>

<p><b>gboolean scp_tree_store_search(ScpTreeStore *store, gboolean sublevels, gboolean
linear_order, GtkTreeIter *iter, GtkTreeIter *parent, gint column, ...);</b></p>

<div>Search the rows under parent for one whose column data matches a value.</div>
<div class="tab">sublevels = <tt>FALSE</tt>: search only the rows directly under parent<br>
sublevels = <tt>TRUE</tt>: search all children<br>
linear_order: always return the first match<br>
parent = <tt>NULL</tt>: search the top-level rows<br>
...: value to compare with, must match the column type.</div>
<p>If column is the current sort column, it's compare function is the default one, and
linear_order is <tt>FALSE</tt>, binary search will be used. Aside from that, the column
compare function is ignored, because it requires an iterator, not a value. For string columns,
utf8_collate is taken into account.</p>

<hr>

<h3><a name="ScpTreeStoreTraverseFunc">ScpTreeStoreTraverseFunc()</a></h3>

<p><b>gint ScpTreeStoreTraverseFunc(ScpTreeStore *store, GtkTreeIter *iter, gpointer gdata);
</b></p>

<div>The callback function type for scp_tree_store_traverse(). Return values:</div>
<div class="tab"><tt>TRUE</tt> (or another positive value): stop iterating<br>
<tt>FALSE</tt>: continue iterating<br>
<tt>-1</tt> (or another negative value): remove the current element and continue.</div>

<h3><a name="scp_tree_store_traverse">scp_tree_store_traverse()</a></h3>

<p><b>gboolean scp_tree_store_traverse(ScpTreeStore *store, gboolean sublevels, GtkTreeIter
*iter, GtkTreeIter *parent, ScpTreeStoreTraverseFunc func, gpointer gdata);</b></p>

<div>Traverse the rows under parent.</div>
<div class="tab">iter: optional, may be NULL<br>
parent = NULL: traverse the top-level rows.</div>
<p></p>
<div>Return values:</div>
<div class="tab"><tt>TRUE</tt> and valid iter if func returns <tt>TRUE</tt> (or
another positive value)<br>
<tt>FALSE</tt> and invalid iter otherwise.</div>
<p></p>

<hr>

<h3><a name="scp_tree_store_register_dynamic">scp_tree_store_register_dynamic()</a></h3>

<p><b>scp_tree_store_register_dynamic(void);</b></p>

<p>ScpTreeStore is written in a way that allows unloading the module which defines it,
provided that:</p>

<ul>
<li>there are no stores in use</li>
<li>no attempts to create a store while the module is unloaded</li>
<li>this function is invoked once each time the module is (re)loaded, for example in it's
startup code.</li>
</ul>

<hr>

<h3><a name="properties">Properties</a></h3>

<p>&quot;sublevels&quot; (gboolean, read/construct) - whether the store supports
sublevels.</p>

<p>&quot;toplevel-reserved&quot; (gint, read/write) - number of pointers to preallocate for
the top-level array. Default = <tt>0</tt>, GPtrArray allocates minimum <tt>16</tt>.</p>

<p>&quot;sublevel-reserved&quot; (gint, read/write) - number of pointers to preallocate the
sublevel arrays. Default = <tt>0</tt>, GPtrArray allocates minimum <tt>16</tt>.</p>

<p>&quot;sublevel-discard&quot; (gboolean, read/write) - whether to discard a sublevel array
when the last row from that level is removed. Default = <tt>FALSE</tt>.</p>

<p>The arrays are allocated when the first row at that level is being inserted. Setting
&quot;-reserved&quot; has no effect on any currently allocated arrays. The top-level array is
discarded only when the store is finalized. Thus, setting &quot;toplevel-reserved&quot; after
at least one row was inserted in the store has no effect.</p>

<h3><a name="notes">Notes</a></h3>

<h3>Non-persistent iterators</h3>

<p>ScpTreeStore's iterators are non-persistent - that is, each operation which changes the
rows order may affect the iterators at that level. More specifically, the iterators work like
array indexes: for you have an iter_a for the 5th row, and remove the 3rd row, iter_a will now
point to the new 5th row (former 6th), or will become invalid, if there were 5 rows. Using
invalid iterators may generate warning messages, or simply crash the application (that applies
to GtkTreeStore as well).</p>

<p>Obviously, appending to an <i>un</i>sorted store will not affect the existing iterators,
and you can predict the effects of all other operations - even for sorted stores, using
scp_tree_store_tell(). But it's easy to make mistakes, so if you need a persistent reference
to a row, it's better to try GtkTreeRowReference.</p>

<p>GtkTreeModelFilter and GtkTreeModelSort use persistent iterators (if available) to cache
their GtkTreeRowReference-s. Although ScpTreeStore has a very fast path to iter, and is
generally quite fast, these models may still be slower.</p>

<h3>top-level &quot;rows-reordered&quot;</h3>

<p><b>void user_function(GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
gpointer new_order, gpointer user_data);</b></p>

<p>From the GtkTreeModel documentation (at least gtk+ &lt;= 3.6.4):</p>

<p>&quot;iter : a valid GtkTreeIter pointing to the node whose&quot; [children have been
reordered?]</p>

<p>When the top-level rows are being reordered, such iter may not be valid, because it must
point above the top. GtkTreeStore pass a pseudo-valid iter which is suitable for a few
operations, and so does ScpTreeStore, but you should not rely on that and check for path
depth = <tt>0</tt> instead.</p>

<hr>

<h3><a name="speed">Speed</a></h3>

<p>An unsorted ScpTreeStore is faster than GtkTreeStore, but the difference is not big (20%
on average, depending on the operation and number of elements), and not significant for stores
with less than 10000 elements (except foreach or lots of searches). Normally, inserting into
and removing from a list is faster than using an array, but all store operations must emit a
signal, containing the row path (element indexes), which negates this advantage.</p>

<p>The sorted gtk+ stores are slow, since they are based on lists, and sorting by a string
with utf-8 collation makes them even slower.</p>

<table>
<tr><th>Store sorted by double</th><th>Row count</th><th>Times</th><th>GtkTreeStore</th>
<th>ScpTreeStore</th></tr>

<tr><td>&nbsp;</td><td></td><td></td><td></td><td></td></tr>
<tr><td>pre/apppend + set     </td><td>250</td><td>   </td><td>0.015</td><td>0.003</td></tr>
<tr><td>pre/append with values</td><td>250</td><td>   </td><td>0.014</td><td>0.001</td></tr>
<tr><td>binary search         </td><td>250</td><td>250</td><td>n/a  </td><td>0.000</td></tr>
<tr><td>unsorted linear search</td><td>250</td><td>250</td><td>0.035</td><td>0.001</td></tr>

<tr><td>&nbsp;</td><td></td><td></td><td></td><td></td></tr>
<tr><td>pre/apppend + set     </td><td>2500</td><td>    </td><td>1.321</td><td>0.041</td></tr>
<tr><td>pre/append with values</td><td>2500</td><td>    </td><td>1.285</td><td>0.016</td></tr>
<tr><td>binary search         </td><td>2500</td><td>2500</td><td>n/a  </td><td>0.001</td></tr>
<tr><td>unsorted linear search</td><td>2500</td><td>770 </td><td>1.031</td><td>0.041</td></tr>

<tr><td colspan="5" class="empty">&nbsp;</td></tr>
<tr><th>Store sorted by string</th><th>Row count</th><th>Times</th><th>GtkTreeStore</th>
<th>ScpTreeStore</th></tr>

<tr><td>&nbsp;</td><td></td><td></td><td></td><td></td></tr>
<tr><td>pre/apppend + set     </td><td>250</td><td>   </td><td>0.088</td><td>0.012</td></tr>
<tr><td>pre/append with values</td><td>250</td><td>   </td><td>0.084</td><td>0.008</td></tr>
<tr><td>binary search         </td><td>250</td><td>250</td><td>n/a  </td><td>0.005</td></tr>
<tr><td>unsorted linear search</td><td>250</td><td>250</td><td>0.220</td><td>0.102</td></tr>

<tr><td>&nbsp;</td><td></td><td></td><td></td><td></td></tr>
<tr><td>pre/apppend + set     </td><td>2500</td><td>    </td><td>8.740</td><td>0.193</td></tr>
<tr><td>pre/append with values</td><td>2500</td><td>    </td><td>8.913</td><td>0.130</td></tr>
<tr><td>binary search         </td><td>2500</td><td>2500</td><td>n/a  </td><td>0.076</td></tr>
<tr><td>unsorted linear search</td><td>2500</td><td>770 </td><td>6.807</td><td>3.186</td></tr>

</table>

<p>The full speed test is <a href="fullspeed.html">here</a>. In general, a large sorted
ScpTreeStore can be used as a normal data structure, unlike a GtkTree/ListStore. And, since
ScpTreeStore is not part of gtk+, you can easily recompile it with -DG_DISABLE_CHECKS, but
don't expect any significant difference.</p>

<hr>

<h3><a name="bugs">Bugs</a></h3>

<p>Incomplete Glade support. ScpTreeStore-s can be included in .glade files with a text
editor, but saving such files with Glade will discard the stores. I don't plan to fix this.
</p>

<p>The linguistically correct string comparision is not well tested.</p>

<p>Report bugs to <tt>&lt;dimitar.zhekov@gmail.com&gt;</tt>.</p>

<h3><a name="copyright">Copyright</a></h3>

<p>Parts of DND, most defensive programming checks and most of the buildable implementation
are from GtkTreeStore.</p>

<p><b>GtkTreeStore, Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford</b></p>

<p>ScpTreeStore was written for the Scope plugin of Geany light IDE, which relies heavily on
stores.</p>

<p><b>ScpTreeStore 0.86.1, Copyright (C) 2014 Dimitar Toshkov Zhekov</b></p>

<p>ScpTreeStore is distributed under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 2 of the License, or (at your option) any
later version.</p>

</body>
</html>
